;;; init-keymaps.el --- Keymap settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

;; Displays available keybindings in popup
(use-package which-key
  :ensure nil
  :hook (elpaca-after-init . which-key-mode)
  :custom
  (which-key-popup-type 'side-window)
  (which-key-side-window-location 'bottom) ; 显示位置
  (which-key-side-window-max-height 12)    ; 最大高度
  (which-key-min-display-lines 2)          ; 最小行数
  (which-key-dont-use-unicode nil))        ; 使用 unicode 字符

;; Create transient pop-up menus for keybindings
(use-package transient
  :ensure t
  :config
  (transient-define-prefix +transient/note ()
    "Denote Transient"
    ["Denote Commands"
     ("d" "Create notes after set date"  denote-date)
     ("l" "Link notes to current buffer" denote-link-or-create)
     ("n" "Create default notes"         denote)
     ("s" "Create notes after set path"  denote-subdirectory)
     ("t" "Create notes after set type"  denote-type)])
  
  (transient-define-prefix +transient/lsp-bridge ()
    "LSP-Bridge Transient Menu"
    ["LSP Commands"
     [("r" "         Find references" lsp-bridge-find-references)
      ("i" "         Find impl"       lsp-bridge-find-impl)
      ("t" "         Find type"       lsp-bridge-find-type-def)
      ("<return>" "  Find def"        lsp-bridge-find-def)
      ("S-<return>" "Find def return" lsp-bridge-find-def-return)]
     [("a" "Code action"              lsp-bridge-code-action)
      ("d" "Document buffer"          lsp-bridge-show-documentation)
      ("D" "Document"                 lsp-bridge-popup-documentation)
      ("c" "Copy diagnostics"         +flymake/copy-flymake-diagnostic-at-point)
      ("p" "Peek definition"          lsp-bridge-peek)
      ("R" "Rename"                   lsp-bridge-rename)]])

  (transient-define-prefix +transient/org()
    "Org Mode Transient Menu"
    ["Org Mode Commands"
     ("d" "org-download-delete"       org-download-delete)
     ("f" "table.el-fill"             ftable-fill)
     ("h" "org-insert-header"         +org/org-insert-header)
     ("i" "org-select-images"         +org/org-select-images)
     ("p" "org-toggle-inline-images"  org-toggle-inline-images)
     ("t" "org-table-create"          org-table-create)
     ("y" "org-download-yank"         org-download-yank)]))

(use-package hydra
  :ensure t
  :custom-face
  (child-frame-border ((t (:background "gray"))))
  :custom
  (hydra-hint-display-type 'posframe)
  :config
  ;; From: github.com/oracleyue/.emacs.d/blob/d6bb6bd67f04cca09472b5692eabcee577e09ece/init/init-keyhelper.el#L37
  (defun hydra-set-posframe-show-params (origin-fun &rest args)
    "Set hydra-posframe style."
    (setq hydra-posframe-show-params
          `(:lines-truncate t
                            :border-width 2
                            :poshandler posframe-poshandler-frame-center)))

  (advice-add 'hydra-posframe-show :before #'hydra-set-posframe-show-params))

;; Simplify hydra creation
(use-package pretty-hydra
  :ensure t
  :custom
  (pretty-hydra-default-title-body-format-spec "\n\n %s\n%s\n")
  :config
  ;; From: github.com/seagle0128/.emacs.d/blob/master/lisp/init-hydra.el
  (defun pretty-hydra-title (title &optional icon-type icon-name face height v-adjust)
    "Add an icon in the hydra title."
    (let ((face (or face 'mode-line-emphasis))
          (height (or height 1.2))
          (v-adjust (or v-adjust 0.0)))
      (concat
       (when (and (or (featurep 'nerd-icons)
                      (require 'nerd-icons nil t)) icon-type icon-name)
         (let ((f (intern (format "nerd-icons-%s" icon-type))))
           (when (fboundp f)
             (concat
              (apply f (list icon-name :face face :height height :v-adjust v-adjust))
              " "))))
       (propertize title 'face face))))
  
  (pretty-hydra-define window-manager-hydra
    (:color amaranth :quit-key ("q" "C-g" "RET" "ESC")
            :foreign-keys warn
            :title (pretty-hydra-title "Window Manager" 'faicon "nf-fa-window_restore"))
    ("Action"
     (("t" #'+window/toggle-window-split "toggle split direction")
      ("o" #'other-window "select other window")
      ("_" #'+window/split-window-below "split window below")
      ("|" #'+window/split-window-right "split window right")
      ("0" #'delete-window "delete window"))

     "Move"
     (("h" buf-move-left  "move to left")
      ("j" buf-move-down  "move to down")
      ("k" buf-move-up    "move to up")
      ("l" buf-move-right "move to right"))

     "Resize"
     (("=" windresize-balance-windows "balance")
      ("<up>"    windresize-up    "   up")
      ("<down>"  windresize-down  " down")
      ("<left>"  windresize-left  " left")
      ("<right>" windresize-right "right")
      ("S-<up>"    windresize-up-fixed    "   keep-width up")
      ("S-<down>"  windresize-down-fixed  " keep-width down")
      ("S-<left>"  windresize-left-fixed  " keep-width left")
      ("S-<right>" windresize-right-fixed "keep-width right")))))

;; 快捷键配置
(use-package general
  :ensure t
  :config
  (if (eq system-type 'windows-nt)
      (general-define-key
       :keymaps 'override
       "C-,"   #'+dict/sqlite-query-word
       "C-M-y" #'+org/org-download-clipboard-windows
       "<f7>"    #'shell-pop)
    (general-define-key
     :keymaps 'override
     "<f7>"  #'vterm-toggle-cd          ; 打开 vterm
     "C-,"   #'sdcv-search-pointer+     ; 查询光标所在单词
     "C-M-y" #'org-download-clipboard))

  ;; global
  (general-define-key
   :keymaps 'override
   "<f4>"    #'dirvish-side             ; 侧边打开 dirvish
   "C-x C-b" #'ibuffer                  ; buffer 管理
   "C-c a"   #'org-agenda               ; org-agenda

   ;; avy
   "C-:"   #'avy-goto-char
   "C-'"   #'avy-goto-char-2
   "M-g l" #'avy-goto-line
   "M-g e" #'avy-goto-word-0
   "M-g w" #'avy-goto-word-1

   ;; helpful
   "C-h C-d" #'helpful-at-point
   "C-h F"   #'helpful-function
   [remap describe-function] #'helpful-callable ; C-h f
   [remap describe-variable] #'helpful-variable ; C-h v
   [remap describe-key]      #'helpful-key      ; C-h k
   [remap describe-command]  #'helpful-command  ; C-h c
   [remap describe-symbol]   #'helpful-symbol   ; C-h s

   ;; magit
   "C-x g"   #'magit-status

   ;; embark
   "C-."   #'embark-act  ; 根据光标所在上下文提供操作菜单
   "C-h b" #'embark-bindings
   "C-h B" #'embark-bindings-at-point

   ;; 编辑增强
   "C-="   #'er/expand-region
   "C-;"   #'smart-comment
   "C-c ;" #'comment-box
   "C-/"   #'undo-fu-only-undo
   "C-?"   #'undo-fu-only-redo
   "C-x u" #'vundo
   "C->" #'mc/mark-next-like-this
   "C-<" #'mc/mark-previous-like-this
   "C-`" #'mc/unmark-previous-like-this
   "C-~" #'mc/unmark-next-like-this
   "C-o" #'+edit/open-line-below
   "C-a" #'+edit/crux-move-beginning-of-line
   "C-w" #'+edit/safe-kill-region
   "C-x <backspace>" #'+edit/backward-kill-line
   [remap kill-ring-save] #'easy-kill
   [remap mark-sexp]      #'easy-mark

   ;; 搜索
   "C-S-s" #'color-rg-search-symbol-in-project ; 跨文件搜索文本
   "C-s"   #'consult-line
   "C-c f" #'consult-fd
   "C-c r" #'recentf                    ; 搜索最近打开的文件

   ;; 分屏相关
   "C-_"   #'+window/split-window-below  ; 上下分屏
   "C-|"   #'+window/split-window-right  ; 左右分屏
   "C-c w" #'window-manager-hydra/body   ; 窗口管理其他快捷键
   "C-c <left>"  #'winner-undo           ; 撤销窗口布局
   "C-c <right>" #'winner-redo           ; 恢复窗口布局

   ;; 笔记相关
   "C-c n" #'+transient/note
   "C-c c" #'org-capture
   "C-c i" #'+hunspell/add-word)

  (general-define-key
   :keymaps 'eshell-mode-map
   "C-l" #'+eshell/clear
   "C-d" #'+eshell/close)

  (general-define-key
   :keymaps '(prog-mode-map sgml-mode-map nxml-mode-map)
   "C-c C-r" #'quickrun-shell              ; 运行代码
   "<f8>"    #'compile                     ; 编译
   "M-g f"   #'consult-flymake             ; flymake 诊断查询
   "C-M-l"   #'format-all-region-or-buffer ; 格式化
   "C-\""    #'+transient/lsp-bridge)      ; lsp-bridge 菜单

  ;; minibuffer
  ;; 类似 `(define-key some-map "f" '("foo" . command-foo))' 的写法来源于
  ;; github.com/justbur/emacs-which-key?tab=readme-ov-file#keymap-based-replacement
  ;; general 同样支持: (general-def "<key>" '("<description>" . <definition>))
  (general-define-key
   :keymaps 'embark-region-map
   "C-;" #'("iedit" . iedit-mode)
   "y"   #'("fanyi" . +fanyi/fanyi-selected-text))

  (general-define-key
   :keymaps 'embark-identifier-map
   "C-;" #'("iedit" . iedit-mode)
   "y"   #'("fanyi" . +fanyi/fanyi-at-point))
  :custom
  ;; 使用general-define-key时，如不指定states或keymaps，默认使用下列值
  (general-default-states nil)
  (general-default-keymaps 'global))

(provide 'init-keymap)
;;; init-keymaps.el ends here
